/**  
 * Copyright (c) 2010 University of Pennsylvania.
 *     All rights reserved.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an "AS
 *  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 *  express or implied.  See the License for the specific language
 *  governing permissions and limitations under the License.
 *
 */
 
void page_init(PAGE *p)
{
  p->page_len = 0; p->tuple_count = 0;
}

uint8_t page_addtuple(PAGE *p, uint8_t tuple_len, uint16_t *tuple)
{
  int i;

  if (p->tuple_count >= MAX_TUPLES || p->page_len+tuple_len*sizeof(uint16_t)+1 > MAX_PAGELEN)
    return 1;

  if (p->tuple_count == 0)
  {
    p->tuple_count = 1;
    p->page_len = tuple_len*sizeof(uint16_t);
    memcpy(p->page, tuple, p->page_len);
  } else {
    for (i = 1; i < p->tuple_count; i++)
      p->page[p->page_len-i+tuple_len*sizeof(uint16_t)+1] = p->page[p->page_len-i];

    memcpy(&p->page[p->page_len-p->tuple_count+1], tuple, tuple_len*sizeof(uint16_t));
    p->page[p->page_len-p->tuple_count+1+tuple_len*sizeof(uint16_t)] =
      (p->page_len-p->tuple_count+1)/sizeof(uint16_t);
    p->tuple_count++;
    p->page_len += tuple_len*sizeof(uint16_t)+1;
  }

  return 0;
}

uint8_t page_gettupleofs(PAGE *p, uint8_t tuple_index)
{
  return (tuple_index == 0) ? 0 : p->page[p->page_len-tuple_index]*sizeof(uint16_t);
}

uint8_t page_deletetuple(PAGE *p, uint8_t tuple_index)
{
  int i;
  uint8_t tuple_ofs, d;

  if (tuple_index >= p->tuple_count)
    return 1;

  tuple_ofs = page_gettupleofs(p, tuple_index);
  if (p->tuple_count == 1)
    p->page_len = 0;
  else {
    d = ((tuple_index == p->tuple_count-1) ? p->page_len-p->tuple_count :
      page_gettupleofs(p, tuple_index+1)) - tuple_ofs;
    for (i = tuple_ofs; i < p->page_len-p->tuple_count; i++)
      p->page[i] = p->page[i+d];
    for (i = p->page_len-p->tuple_count; i < p->page_len-tuple_index; i++)
      p->page[i] = p->page[i+1]-d/sizeof(uint16_t);
    p->page_len -= d+1;
  }
  p->tuple_count--;

  return 0;
}

uint8_t page_gettuple(PAGE *p, uint8_t tuple_index, uint8_t *tuple_len, uint16_t *tuple)
{
  uint8_t tuple_ofs;

  if (tuple_index >= p->tuple_count)
    return 1;

  tuple_ofs = page_gettupleofs(p, tuple_index);
  *tuple_len = (p->tuple_count == 0) ? p->page_len : ((tuple_index == p->tuple_count-1) ?
    (p->page_len-tuple_index-tuple_ofs) : (page_gettupleofs(p, tuple_index+1)-tuple_ofs));

  memcpy(tuple, &p->page[tuple_ofs], *tuple_len);
  *tuple_len /= sizeof(uint16_t);

  return 0;
}

void marshal_page(uint8_t **pos, PAGE *p)
{
  int i;
  uint8_t pr, d;
  int8_t diff = 0;

  for (i = 0; i < p->page_len-p->tuple_count; i += sizeof(uint16_t))
    if (*(uint16_t *) &p->page[i] < 0x80)
      diff++;
    else if (*(uint16_t *) &p->page[i] >= 0x4000)
      diff--;

  marshal_byte(pos, p->tuple_count | ((diff > 0) << 7));
  if (p->tuple_count > 0)
  {
    marshal_byte(pos, p->page_len);
    for (i = ((p->tuple_count == 1) ? sizeof(uint16_t) : 0); i < p->page_len-p->tuple_count; i += sizeof(uint16_t))
      if (diff > 0)
        marshal_wordvb(pos, *(uint16_t *) &p->page[i]);
      else marshal_word(pos, *(uint16_t *) &p->page[i]);

    if (p->tuple_count > 1)
    {
      pr = 0; d = 0;
      for (i = 1; i < p->tuple_count; i++)
      {
        if (i % 2)
          d = p->page[p->page_len-i]-pr;
        else marshal_byte(pos, ((p->page[p->page_len-i]-pr) << 4) | d);
        pr = p->page[p->page_len-i];
      }
      if (p->tuple_count % 2 == 0)
        marshal_byte(pos, d);
    }
  }
}

void unmarshal_page(uint8_t **pos, PAGE *p, ADDRESS source_node)
{
  int i;
  uint8_t pr, d, is_vb;

  p->tuple_count = unmarshal_byte(pos);
  is_vb = (p->tuple_count >> 7) & 0x01;
  p->tuple_count &= 0x7f;
    
  if (p->tuple_count > 0)             
  {
    p->page_len = unmarshal_byte(pos);
    for (i = ((p->tuple_count == 1) ? sizeof(uint16_t) : 0); i < p->page_len-p->tuple_count+1; i += sizeof(uint16_t))
      *(uint16_t *) &p->page[i] = (is_vb ? unmarshal_wordvb(pos) : unmarshal_word(pos));
    if (p->tuple_count == 1)
      *(uint16_t *) &p->page[0] = source_node;

    if (p->tuple_count > 1)
    {
      pr = 0; d = 0;
      for (i = 1; i < p->tuple_count; i++)
      {
        if (i % 2)
          d = unmarshal_byte(pos);
        p->page[p->page_len-i] = pr+(d & 0x0f);
        pr = p->page[p->page_len-i];
        d >>= 4;
      }
    }
  }
}


